import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
import { AxiosCanceler } from './axiosCancel'
import { isFunction } from '/@/utils/is'
import { cloneDeep } from 'lodash-es'

import { CreateAxiosOptions, RequestOptions, UploadFileParams } from './types'
import { ContentTypeEnum } from '/@/enums/httpEnum'
import { Result } from '/@/api/model/baseModel'
import { message as antdMessage } from 'antd'

export * from './axiosTransform'

/**
 * @description:  axios模块
 */
export class VAxios {
  private axiosInstance: AxiosInstance
  private readonly options: CreateAxiosOptions

  constructor(options: CreateAxiosOptions) {
    this.options = options
    this.axiosInstance = axios.create(options)
    this.setupInterceptors()
  }

  /**
   * @description:  创建axios实例
   */
  private createAxios(config: CreateAxiosOptions): void {
    this.axiosInstance = axios.create(config)
  }

  private getTransform() {
    const { transform } = this.options
    return transform
  }

  getAxios(): AxiosInstance {
    return this.axiosInstance
  }

  /**
   * @description: 重新配置axios
   */
  configAxios(config: CreateAxiosOptions) {
    if (!this.axiosInstance) {
      return
    }
    this.createAxios(config)
  }

  /**
   * @description: 设置通用header
   */
  setHeader(headers: any): void {
    if (!this.axiosInstance) {
      return
    }
    Object.assign(this.axiosInstance.defaults.headers, headers)
  }

  /**
   * @description: 拦截器配置
   */
  private setupInterceptors() {
    const transform = this.getTransform()
    if (!transform) {
      return
    }

    const {
      requestInterceptors,
      requestInterceptorsCatch,
      responseInterceptors,
      responseInterceptorsCatch
    } = transform

    const axiosCanceler = new AxiosCanceler()

    // 请求拦截器配置处理
    this.axiosInstance.interceptors.request.use(
      (config: AxiosRequestConfig) => {
        // If cancel repeat request is turned on, then cancel repeat request is prohibited
        const {
          headers: { ignoreCancelToken } = { ignoreCancelToken: false }
        } = config
        !ignoreCancelToken && axiosCanceler.addPending(config)
        if (requestInterceptors && isFunction(requestInterceptors)) {
          config = requestInterceptors(config)
        }
        return config
      },
      undefined
    )

    // 请求拦截器错误捕获
    requestInterceptorsCatch &&
      isFunction(requestInterceptorsCatch) &&
      this.axiosInstance.interceptors.request.use(
        undefined,
        requestInterceptorsCatch
      )

    // 响应结果拦截器处理
    this.axiosInstance.interceptors.response.use(
      (res: AxiosResponse<Result<any>>) => {
        res && axiosCanceler.removePending(res.config)
        if (responseInterceptors && isFunction(responseInterceptors)) {
          res = responseInterceptors(res)
        }
        return res
      },
      undefined
    )

    // 响应结果拦截器错误捕获
    responseInterceptorsCatch &&
      isFunction(responseInterceptorsCatch) &&
      this.axiosInstance.interceptors.response.use(
        undefined,
        responseInterceptorsCatch
      )
  }

  /**
   * @description:  文件上传
   */
  uploadFile<T = any>(
    config: AxiosRequestConfig,
    params: UploadFileParams
  ): Promise<T> {
    const formData = new window.FormData()

    if (params.data) {
      Object.keys(params.data).forEach(key => {
        if (!params.data) return
        const value = params.data[key]
        if (Array.isArray(value)) {
          value.forEach(item => {
            formData.append(`${key}[]`, item)
          })
          return
        }

        formData.append(key, params.data[key])
      })
    }

    formData.append(params.name || 'file', params.file, params.filename)

    return this.axiosInstance
      .request<T>({
        ...config,
        method: 'POST',
        data: formData,
        headers: {
          'Content-type': ContentTypeEnum.FORM_DATA,
          ignoreCancelToken: true
        }
      })
      .then(res => {
        return Promise.resolve(res.data)
      })
      .catch(error => {
        return Promise.reject(error)
      })
  }

  /**
   * @description:   请求方法
   */
  request<T = any>(
    config: AxiosRequestConfig,
    options?: RequestOptions
  ): Promise<T> {
    let conf: AxiosRequestConfig = cloneDeep(config)
    const transform = this.getTransform()

    const { requestOptions } = this.options
    const opt: RequestOptions = Object.assign({}, requestOptions, options)

    const { beforeRequestHook, requestCatch, transformRequestData } =
      transform || {}
    if (beforeRequestHook && isFunction(beforeRequestHook)) {
      conf = beforeRequestHook(conf, opt)
    }

    return new Promise((resolve, reject) => {
      this.axiosInstance
        .request<any, AxiosResponse<Result<any>>>(conf)
        .then((res: AxiosResponse<Result<any>>) => {
          if (transformRequestData && isFunction(transformRequestData)) {
            const ret = transformRequestData(res, opt)
            ret !== '' ? resolve(ret) : reject(new Error('request error!'))
            return
          }
          resolve((res as unknown) as Promise<T>)
        })
        .catch((e: Error) => {
          if (requestCatch && isFunction(requestCatch)) {
            requestCatch(e)
            return
          }
          antdMessage.error({
            content: e.message,
            className: 'custom-class',
            style: {
              borderRadius: '7px',
              marginTop: '50%'
            },
            duration: 5
          })
        })
    })
  }
}
